﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;

using LowLevelGraphics.ColorSpaces;
using LowLevelGraphics.Filter;

namespace LowLevelGraphics
{
    /// <summary>
    /// Processing modes
    /// </summary>
    public enum PROCESSING_MODE
    {
        /// <summary>
        /// Default Processing Mode
        /// </summary>
        Default,
        /// <summary>
        /// Take color value (ARGB)
        /// </summary>
        Color,
        /// <summary>
        /// Take brightness value
        /// </summary>
        Brightness,
        /// <summary>
        /// Take luminance value
        /// </summary>
        Luminance,
        /// <summary>
        /// Take hue value
        /// </summary>
        Hue,
        /// <summary>
        /// Take saturation value
        /// </summary>
        Saturation,
        /// <summary>
        /// Take sum of R + G + B
        /// </summary>
        SumRGB,
        /// <summary>
        /// Take sum of R + G + B + A
        /// </summary>
        SumARGB,
        /// <summary>
        /// Take multiplicatio of R * G * B
        /// </summary>
        MulRGB,
        /// <summary>
        /// Tak product of A * R * G * B
        /// </summary>
        MulARGB,
        /// <summary>
        /// Take value of A
        /// </summary>
        A,
        /// <summary>
        /// Take value of R
        /// </summary>
        R,
        /// <summary>
        /// Take value of G
        /// </summary>
        G,
        /// <summary>
        /// Take value of B
        /// </summary>
        B,
        /// <summary>
        /// -
        /// </summary>
        None,
        /// <summary>
        /// Take color count of current pixel
        /// </summary>
        ColorCount,
        /// <summary>
        /// Take gray color value (ARGB of Gray)
        /// </summary>
        GrayColor,
        /// <summary>
        /// Take gray color value, e.g. if ARGB is 0xFFFFFF take 0xFF
        /// </summary>
        GrayValue,
        /// <summary>
        /// Take color constancy value
        /// </summary>
        ColorConstancy,
        /// <summary>
        /// Take ARGB value
        /// </summary>
        ToArgb,
        /// <summary>
        /// Take INT rgb value
        /// </summary>
        ToIntRgb,
        /// <summary>
        /// Take per channel brightness
        /// </summary>
        PerChannelBrightness,
        /// <summary>
        /// Take per channel luminance
        /// </summary>
        PerChannelLuminance,
        /// <summary>
        /// Take per channel saturation
        /// </summary>
        PerChannelSaturation,
        /// <summary>
        /// Take per channel Hue
        /// </summary>
        PerChannelHue
    }

    /// <summary>
    /// Bool operator
    /// </summary>
    public enum BoolOperator
    {
        /// <summary>
        /// 
        /// </summary>
        AND,
        /// <summary>
        /// 
        /// </summary>
        OR,
        /// <summary>
        /// 
        /// </summary>
        XOR,
        /// <summary>
        /// 
        /// </summary>
        NAND,
        /// <summary>
        /// 
        /// </summary>
        NOR,
        /// <summary>
        /// 
        /// </summary>
        XNOR
    }

    /// <summary>
    /// Public delegates
    /// </summary>
    /// <param name="_color"></param>
    /// <returns></returns>
    public delegate float ColorCalculation(Color _color);

    /// <summary>
    /// 
    /// </summary>
    public partial class Definitions
    {
        private static int m_nHalfRGB = 127;
        private static Dictionary<EXPERIMENTAL1, ColorCalculation> m_aDict = new Dictionary<EXPERIMENTAL1, ColorCalculation>();
        private static Dictionary<PROCESSING_MODE, ColorCalculation> m_aDictProcessingMode = new Dictionary<PROCESSING_MODE, ColorCalculation>();
        private static Dictionary<Color, Color> m_aPseudoColorList = new Dictionary<Color, Color>();
        private static Color[] m_aKnownColors = null;

        private static double[] m_aLog = new double[256];
        private static double[] m_aLn = new double[256];
        private static double[] m_aCos = new double[360];
        private static double[] m_aSin = new double[360];
        private static double[] m_aArcTan = new double[360];

        /// <summary>
        /// Initializes the <see cref="Definitions"/> class.
        /// </summary>
        static Definitions()
        {
            Color[] aColor = InitializeKnownColors();
            m_aKnownColors = aColor;

            m_aDict.Add(EXPERIMENTAL1.A, new ColorCalculation(GetA));
            m_aDict.Add(EXPERIMENTAL1.R, new ColorCalculation(GetR));
            m_aDict.Add(EXPERIMENTAL1.G, new ColorCalculation(GetG));
            m_aDict.Add(EXPERIMENTAL1.B, new ColorCalculation(GetB));
            m_aDict.Add(EXPERIMENTAL1.SATURATION, new ColorCalculation(GetSaturation));
            m_aDict.Add(EXPERIMENTAL1.BRIGHTNESS, new ColorCalculation(GetBrightness));
            m_aDict.Add(EXPERIMENTAL1.HUE, new ColorCalculation(GetHue));
            m_aDict.Add(EXPERIMENTAL1.LUMINANCE, new ColorCalculation(GetLuminance));
            m_aDict.Add(EXPERIMENTAL1.MUL, new ColorCalculation(GetMul));
            m_aDict.Add(EXPERIMENTAL1.SUM, new ColorCalculation(GetSumRGB));
            m_aDict.Add(EXPERIMENTAL1.GRAYVALUE, new ColorCalculation(GetGray));
            m_aDict.Add(EXPERIMENTAL1.TOARGB, new ColorCalculation(GetARGB));
            m_aDict.Add(EXPERIMENTAL1.CHANNELSUM, new ColorCalculation(GetSumRGB));

            m_aDictProcessingMode.Add(PROCESSING_MODE.ColorCount, new ColorCalculation(GetCount));
            m_aDictProcessingMode.Add(PROCESSING_MODE.Color, new ColorCalculation(GetColor));
            m_aDictProcessingMode.Add(PROCESSING_MODE.A, new ColorCalculation(GetA));
            m_aDictProcessingMode.Add(PROCESSING_MODE.R, new ColorCalculation(GetR));
            m_aDictProcessingMode.Add(PROCESSING_MODE.G, new ColorCalculation(GetG));
            m_aDictProcessingMode.Add(PROCESSING_MODE.B, new ColorCalculation(GetB));
            m_aDictProcessingMode.Add(PROCESSING_MODE.Saturation, new ColorCalculation(GetSaturation));
            m_aDictProcessingMode.Add(PROCESSING_MODE.Brightness, new ColorCalculation(GetBrightness));
            m_aDictProcessingMode.Add(PROCESSING_MODE.Hue, new ColorCalculation(GetHue));
            m_aDictProcessingMode.Add(PROCESSING_MODE.Luminance, new ColorCalculation(GetLuminance));
            m_aDictProcessingMode.Add(PROCESSING_MODE.MulRGB, new ColorCalculation(GetMul));
            m_aDictProcessingMode.Add(PROCESSING_MODE.MulARGB, new ColorCalculation(GetMulARGB));
            m_aDictProcessingMode.Add(PROCESSING_MODE.SumRGB, new ColorCalculation(GetSumRGB));
            m_aDictProcessingMode.Add(PROCESSING_MODE.SumARGB, new ColorCalculation(GetSumARGB));
            m_aDictProcessingMode.Add(PROCESSING_MODE.ToArgb, new ColorCalculation(GetARGB));
            m_aDictProcessingMode.Add(PROCESSING_MODE.ToIntRgb, new ColorCalculation(GetIntRGB));
            //m_aDictProcessingMode.Add(PROCESSING_MODE.To3Bytes, new ColorCalculation(Get3Bytes));
            //m_aDictProcessingMode.Add(PROCESSING_MODE.To4Bytes, new ColorCalculation(Get4Bytes));
            m_aDictProcessingMode.Add(PROCESSING_MODE.GrayValue, new ColorCalculation(GetGray));
            m_aDictProcessingMode.Add(PROCESSING_MODE.ColorConstancy, new ColorCalculation(GetColorConstancy));
            m_aDictProcessingMode.Add(PROCESSING_MODE.PerChannelBrightness, new ColorCalculation(GetPerChannelBrightness));
            m_aDictProcessingMode.Add(PROCESSING_MODE.PerChannelHue, new ColorCalculation(GetPerChannelHue));
            m_aDictProcessingMode.Add(PROCESSING_MODE.PerChannelSaturation, new ColorCalculation(GetPerChannelSaturation));
            m_aDictProcessingMode.Add(PROCESSING_MODE.PerChannelLuminance, new ColorCalculation(GetPerChannelLuminance));
            CreatePseudoColorList();

            LogLookUpTable();
        }

        /// <summary>
        /// Create Log / Ln Lookuptable
        /// </summary>
        private static void LogLookUpTable()
        {
            for (int i = 0; i < 256; i++)
            {
                m_aLog[i] = Math.Log(i);
                m_aLn[i] = Math.Log10(i);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private static void SinLookUpTable()
        {
            for (int i = 0; i < 360; i++)
            {
                m_aSin[i] = Math.Sin(i);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private static void CosLookUpTable()
        {
            for (int i = 0; i < 360; i++)
            {
                m_aCos[i] = Math.Cos(i);
            }
        }

        /// <summary>
        /// Gets the auto complete from filter list.
        /// </summary>
        /// <returns></returns>
        public static AutoCompleteStringCollection GetAutoCompleteFromFilterList()
        {
            List<string> aFilter = new List<string>();
            AutoCompleteStringCollection a = new AutoCompleteStringCollection();
            foreach (Type filterType in Definitions.FilterList())
            {
                string sText = filterType.Name;
                a.Add(sText);
            }

            return a;
        }

        /// <summary>
        /// Gets the color spaces.
        /// </summary>
        /// <value>The color spaces.</value>
        public static List<AbstractColorSpace> ColorSpaces
        {
            get
            {
                return new List<AbstractColorSpace>() 
            { 
                new CIELab(Definitions.White), 
                new CIELCH(Definitions.White), 
                new CIELuv(Definitions.White), 
                new CIEXYZ(Definitions.White), 
//                new HCL(Definitions.White), 
                new HSB(Definitions.White), 
//                new HSI(Definitions.White), 
                new HSL(Definitions.White), 
//                new HSV(Definitions.White), 
                new HunterLab(Definitions.White), 
                new IRgBy(Definitions.White), 
                new LMS(Definitions.White), 
                new sRGB(Definitions.White), 
                new XYZ(Definitions.White),
            new YCbCr(Definitions.White),
            new YIQ(Definitions.White),
            new YPbPr(Definitions.White),
            new YUV(Definitions.White),
            new YUV444(Definitions.White),
            new NormalizedRGB(Definitions.White)};
            }
        }

        /// <summary>
        /// Bools the specified b actual value.
        /// </summary>
        /// <param name="bActualValue">if set to <c>true</c> [b actual value].</param>
        /// <param name="bValue">if set to <c>true</c> [b value].</param>
        /// <param name="_boolOperator">The _bool operator.</param>
        /// <returns></returns>
        public static bool Bool(bool bActualValue, bool bValue, BoolOperator _boolOperator)
        {
            bool bTemp = bActualValue;
            switch (_boolOperator)
            {
                case BoolOperator.AND:
                    bTemp &= bValue;
                    break;

                case BoolOperator.OR:
                    bTemp |= bValue;
                    break;

                case BoolOperator.XOR:
                    bTemp ^= bValue;
                    break;

                case BoolOperator.NAND:
                    bTemp &= !bValue;
                    break;

                case BoolOperator.NOR:
                    bTemp |= !bValue;
                    break;

                case BoolOperator.XNOR:
                    bTemp ^= !bValue;
                    break;
            }
            return bTemp;
        }

        /// <summary>
        /// Creates the pseudo color list.
        /// </summary>
        private static void CreatePseudoColorList()
        {
            for (int i = 0; i <= 255; i++)
            {
                m_aPseudoColorList.Add(Color.FromArgb(i, i, i), Color.FromArgb(i, 255 - i, i));
            }
        }

        /// <summary>
        /// returns the default pseudo color list
        /// </summary>
        public static Dictionary<Color, Color> PseudoColorList
        {
            get { return m_aPseudoColorList; }
        }

        /// <summary>
        /// Gets the Assembly
        /// </summary>
        public static Assembly Assembly
        {
            get { return Assembly.GetExecutingAssembly(); }
        }

        /// <summary>
        /// Get chromaticity from CIEXYZ
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public double Chromaticity(Color _color)
        {
            CIEXYZ xyz = new CIEXYZ(_color);
            return (0.4661d * xyz.X + 0.1593d * xyz.Y) / (xyz.Y - 0.15735d * xyz.X + 0.2424d);
        }

        /// <summary>
        /// Gets all filters as an IEnumerable
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Type> FilterList()
        {
            Assembly assembly = Assembly.GetExecutingAssembly();

            Type[] typeOfFilter = assembly.GetTypes();

            List<Type> aType = new List<Type>();

            foreach (Type type in typeOfFilter)
            {
                if (type.Name == "Experimental2")
                {
                }
                Type typeTemp = BaseType(type, typeof(BaseImageFilter));
                if (type.IsAbstract) continue;
                if (type.IsNotPublic) continue;
                //TODO get real base type
                if (typeTemp != typeof(BaseImageFilter)) continue;
                if (type.FullName.ToLower().Contains("graysc"))
                {
                }
                if (type.FullName.ToLower().StartsWith("lowlevelgraphics.filter."))
                {
                    aType.Add(type);
                }
                //TODO: only to debug yet
                else
                {
                }
            }
            return aType;
        }

        /// <summary>
        /// Bases the type.
        /// </summary>
        /// <param name="_type">The _type.</param>
        /// <param name="_matchingType">Type of the _matching.</param>
        /// <returns></returns>
        private static Type BaseType(Type _type, Type _matchingType)
        {
            while (_type.BaseType != null && _type != _matchingType)
            {
                _type = _type.BaseType;
            }
            return _type;
        }

        /// <summary>
        /// returns a half rgb value
        /// </summary>
        public static int HalfRGB
        {
            get { return m_nHalfRGB; }
        }

        /// <summary>
        /// Gets the color calculation dictionary.
        /// </summary>
        /// <value>The color calculation dictionary.</value>
        public static Dictionary<EXPERIMENTAL1, ColorCalculation> ColorCalculationDictionary
        {
            get { return m_aDict; }
        }

        /// <summary>
        /// Gets the color calculation dictionary processing mode.
        /// </summary>
        /// <value>The color calculation dictionary processing mode.</value>
        public static Dictionary<PROCESSING_MODE, ColorCalculation> ColorCalculationDictionaryProcessingMode
        {
            get { return m_aDictProcessingMode; }
        }

        /// <summary>
        /// returns an environment point list (8-Neighbouring)
        /// </summary>
        public static List<Point> EnvironmentPointList = new List<Point>(){
                new Point(-1,-1), 
                new Point(-1,0),
                new Point(-1,1),
                new Point(0,1),
                new Point(1,0),
                new Point(1,-1),
                new Point(1,1),
                new Point(0,-1)
            };

        /// <summary>
        /// returns an environment point list clockwise (8-Neighbouring)
        /// </summary>
        public static List<Point> EnvironmentPointListClockwise = new List<Point>(){
                new Point(-1,-1), 
                new Point(0,-1),
                new Point(1,-1),
                new Point(1,0),
                new Point(1,1),
                new Point(0,1),
                new Point(-1,1),
                new Point(-1,0),
            };

        /// <summary>
        /// returns an environment point list clockwise (8-Neighbouring)
        /// </summary>
        public static List<Point> EnvironmentPointList4Clockwise = new List<Point>(){
            new Point(-1,0),
            new Point(0,-1),
            new Point(0,1),
            new Point(1,0)
        };


        /// <summary>
        /// returns a circular mask
        /// </summary>
        public static int[,] aCircularMask = new int[,]{
                        {-1,-3},{0,-3},{1,-3},
                {-2,-2},{-1,-2},{0,-2},{1,-2},{2,-2},
       {-3,-1},{-2,-1},{-1,-1},{0, -1},{1, -1},{2, -1},{3, -1},
       {-3, 0},{ 2, 0},{-1, 0},{0, 0},{1, 0},{2, 0},{3, 0},
       {-3, 1},{-2, 1},{-1, 1},{0, 1},{1, 1},{2, 1},{3, 1},
               {-2, 2},{-1, 2},{0, 2},{1, 2},{2, 2},
                       {-1, 3},{0, 3},{1,3}
        };

        /// <summary>
        /// Returns white, but not as Color.White because it is no known color
        /// </summary>
        public static Color White = Color.FromArgb(255, 255, 255);

        /// <summary>
        /// Same for black
        /// </summary>
        public static Color Black = Color.FromArgb(0, 0, 0);
        
        /// <summary>
        /// Same for red
        /// </summary>
        public static Color Red = Color.FromArgb(255, 0, 0);
        
        /// <summary>
        /// same for green
        /// </summary>
        public static Color Green = Color.FromArgb(0, 255, 0);

        /// <summary>
        /// same for gray
        /// </summary>
        public static Color Gray = Color.FromArgb(127, 127, 127);

        /// <summary>
        /// same for blue
        /// </summary>
        public static Color Blue = Color.FromArgb(0, 0, 255);
        
        /// <summary>
        /// same for yellow
        /// </summary>
        public static Color Yellow = Color.FromArgb(255, 255, 0);
        
        /// <summary>
        /// same for cyan
        /// </summary>
        public static Color Cyan = Color.FromArgb(0, 255, 255);
        
        /// <summary>
        /// same for magenta
        /// </summary>
        public static Color Magenta = Color.FromArgb(255, 0, 255);

        /// <summary>
        /// Gets the color.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <returns></returns>
        public static float GetColor(Color _color)
        {
            return _color.ToArgb();
        }

        /// <summary>
        /// Returns A-channel of given rgb color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetA(Color _color)
        {
            return (float)_color.A;
        }

        /// <summary>
        /// Returns R-channel of given rgb color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetR(Color _color)
        {
            return _color.R;
        }

        /// <summary>
        /// Returns G-channel of given rgb color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetG(Color _color)
        {
            return _color.G;
        }

        /// <summary>
        /// Returns B-channel of given rgb color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetB(Color _color)
        {
            return _color.B;
        }

        /// <summary>
        /// Returns saturation of given color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetSaturation(Color _color)
        {
            return _color.GetSaturation();
        }

        /// <summary>
        /// returns brightness of given color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetBrightness(Color _color)
        {
            return _color.GetBrightness();
        }

        /// <summary>
        /// returns hue of given color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetHue(Color _color)
        {
            return _color.GetHue();
        }

        /// <summary>
        /// returns luminance of given color
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetLuminance(Color _color)
        {
            return ColorFunctions.GetLuminance(_color);
        }

        /// <summary>
        /// returns a product of all channel components
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetMul(Color _color)
        {
            return (float)(_color.R * _color.G * _color.B);
        }

        /// <summary>
        /// returns a product of all channel components
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetMulARGB(Color _color)
        {
            return (float)(_color.A * _color.R * _color.G * _color.B);
        }

        /// <summary>
        /// returns a 
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetSumRGB(Color _color)
        {
            return (float)(_color.R + _color.G + _color.B);
        }

        /// <summary>
        /// Get sum of all 4 channels: a + r + b + g
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetSumARGB(Color _color)
        {
            return (float)(_color.A + _color.R + _color.G + _color.B);
        }

        /// <summary>
        /// Get gray 
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetGray(Color _color)
        {
            return (float)(ColorFunctions.Gray(_color));
        }

        /// <summary>
        /// return IntRGB
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetIntRGB(Color _color)
        {
            return (float)(ColorFunctions.ToIntRGB(_color));
        }

        /// <summary>
        /// Gets the color constancy.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <returns></returns>
        public static float GetColorConstancy(Color _color)
        {
            return (float)ColorFunctions.GetColorConstancy2(_color, 45);
        }

        /// <summary>
        /// Gets the per channel brightness.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <returns></returns>
        public static float GetPerChannelBrightness(Color _color)
        {
            return (Color.FromArgb(_color.R, 0, 0).GetBrightness()
            + Color.FromArgb(0, _color.G, 0).GetBrightness()
            + Color.FromArgb(0, 0, _color.B).GetBrightness())/3.0f;
        }

        /// <summary>
        /// Gets the per channel hue.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <returns></returns>
        public static float GetPerChannelHue(Color _color)
        {
            return (Color.FromArgb(_color.R, 0, 0).GetHue()
            + Color.FromArgb(0, _color.G, 0).GetHue()
            + Color.FromArgb(0, 0, _color.B).GetHue()) / 3.0f;
        }

        /// <summary>
        /// Gets the count.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <returns></returns>
        public static float GetCount(Color _color)
        {
            return 0;
        }

        /// <summary>
        /// Gets the per channel luminance.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <returns></returns>
        public static float GetPerChannelLuminance(Color _color)
        {
            return (ColorFunctions.GetLuminance(Color.FromArgb(_color.R, 0, 0))
            + ColorFunctions.GetLuminance(Color.FromArgb(0, _color.G, 0))
            + ColorFunctions.GetLuminance(Color.FromArgb(0, 0, _color.B))) / 3.0f;
        }

        /// <summary>
        /// Gets the per channel hue.
        /// </summary>
        /// <param name="_color">The _color.</param>
        /// <returns></returns>
        public static float GetPerChannelSaturation(Color _color)
        {
            return (Color.FromArgb(_color.R, 0, 0).GetSaturation()
            + Color.FromArgb(0, _color.G, 0).GetSaturation()
            + Color.FromArgb(0, 0, _color.B).GetSaturation()) / 3.0f;
        }

        /// <summary>
        /// return Argb
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static float GetARGB(Color _color)
        {
            return (float)_color.ToArgb();
        }

        /// <summary>
        /// Gets 3 bytes
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static byte[] Get3Bytes(Color _color)
        {
            return new byte[] { _color.R, _color.G, _color.B };
        }

        /// <summary>
        /// Gets 4 bytes
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static byte[] Get4Bytes(Color _color)
        {
            return new byte[] { _color.A, _color.R, _color.G, _color.B };
        }
  
        /// <summary>
        /// initializes known colors
        /// </summary>
        /// <returns></returns>
        private static Color[] InitializeKnownColors()
        {
            Array ca = Enum.GetValues(typeof(KnownColor));

            Color[] aColor = new Color[ca.Length];
            int i = 0;
            foreach (KnownColor color in ca)
            {
                aColor[i++] = Color.FromKnownColor(color);
            }
            return aColor;
        }

        /// <summary>
        /// Return known colors
        /// </summary>
        public static Color[] KnownColors
        {
            get { return m_aKnownColors; }
        }
    }
}
